package Utiles;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.util.AbstractMap;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.Scanner;
import java.util.Vector;


import estructuras.common.Almacen;
import estructuras.common.Conexion;
import estructuras.common.Continente;
import estructuras.common.Fecha;
import estructuras.common.Paquete;

public class DataFactory {
	
	public Vector<Continente> getContinentes() {
		return (Vector<Continente>) continentes.clone();
	}
	public Vector<Almacen> getAlmacenes() {
		return (Vector<Almacen>) almacenes.clone();
	}
	public Fecha getFechaInicio() {
		return fechaInicio;
	}
	public Fecha getFechaFin() {
		return fechaFin;
	}
	public Vector<Conexion> getConexiones() {
		return (Vector<Conexion>) conexiones.clone();
	}
	
	Vector< Continente > continentes; 
	Vector< Almacen > almacenes ;
	Fecha fechaInicio , fechaFin;
	Vector< Conexion > conexiones;
	public HashMap<Map.Entry<Almacen, Almacen>, Integer> medias;
	public HashMap<Map.Entry<Almacen, Almacen>, Double> varianzas;
	
	int findAlmacen( String nombre ){
		int id = 0 ;
		for( Almacen a : almacenes ) if( a.getNombre().compareTo( nombre ) == 0 ) {
			break;
		} else { id++; } 
		return id;
	}
	public Almacen findAlmacenPorNombre( String nombre ){
		for( Almacen a : almacenes ) if( a.getNombre().compareTo( nombre ) == 0 ) {
			return a;
		}  
		return null;
	}
	
	public void processFromFiles( String nombreContinentes , String nombreAlmacenes , String nombreConexiones , String nombreParametros) throws Exception {
		File file = new File( nombreContinentes );
		continentes = new Vector<>();
		fechaInicio = new Fecha( 0 , 0 ); fechaFin = new Fecha(0, 0);
		conexiones = new Vector<>();
		almacenes = new Vector<>();	
		
		Scanner in = new Scanner( file );
		while( in.hasNextLine() ){
			String name = in.nextLine();
			continentes.add( new Continente( name ) );
		}
		in.close();
		
		file = new File( nombreParametros );
		in = new Scanner( file );
		int dias = in.nextInt();
		
		fechaFin = new Fecha( dias , 0 );
		
		in.close();
		file = new File( nombreAlmacenes );
		in = new Scanner( file );
		
		while( in.hasNextLine() ){
			String name = in.nextLine();
			String cont = in.nextLine();
			int id = 0;
			for( Continente c : continentes ) if( c.getNombre().compareTo( cont ) == 0 ){
				break;
			} else { id ++; };
			int capacidad = in.nextInt();
			Almacen alm = new Almacen( name , continentes.get( id ) );
			alm.addCambioDeCapacidad( capacidad );
			almacenes.add( alm );
			in.nextLine();
		}
		in.close();
		
		file = new File( nombreConexiones );
		
		in = new Scanner( file );
		while( in.hasNextLine() ){
			String u = in.nextLine() , v = in.nextLine();
			int idu = findAlmacen( u );
			int idv = findAlmacen( v );
			int hora = in.nextInt();
			int capacidad = in.nextInt();
			in.nextLine();
			String pattern = in.nextLine();
			boolean [] freq = new boolean [ 7 ];
			for( int i = 0 ; i < 7 ; i++ )  freq[ i ] = pattern.charAt( i ) == '1';
			Conexion con = new Conexion( almacenes.get( idu ), almacenes.get( idv ), hora, 
										 freq , capacidad );
			conexiones.add( con );
		}
				
	}
	
	//Cuidado con poner inicio 0!!! 
	// se generara data inicial desde [ 0 - inicioSimulacion [
	public void processRandom( int nroContinentes , int nroAlmacenes , int nroMinConexionesPorAlmacen ,
						int nroMaxConexionesPorAlmacen , int nroConexionesDiaPorAlmacen ,
						int capacidadMinimaConexion , int capacidadMaximaConexion , 
						int capacidadMinimaAlmacen , int capacidadMaximaAlmacen ,
						int inicioSimulacion , int finSimulacion,
						int mediaMaxima, double varianzaMaxima){	
		continentes = new Vector<>();
		fechaInicio = new Fecha( inicioSimulacion , 0 ); fechaFin = new Fecha(finSimulacion, 0);
		conexiones = new Vector<>();
		almacenes = new Vector<>();
		medias = new HashMap<>();
		varianzas = new HashMap<>();
		
		Random gen = new Random();
		int gapCapacidad = capacidadMaximaAlmacen - capacidadMinimaAlmacen; // para evitar capacidad 0 
		int gapConex = capacidadMaximaConexion - capacidadMinimaConexion;
		// generar Continentes
		for( int i = 0 ; i < nroContinentes ; i++ ){
			Continente cont = new Continente( "Continente " + ( i + 1 ) );
			continentes.add( cont );
		}
		// generar Almacenes
		for( int i = 0 ; i < nroAlmacenes ; i++ ){
			int id = gen.nextInt( nroContinentes );
			Continente cont = continentes.get( id );
			Almacen alm = new Almacen( "Almacen " + ( i + 1 ) , cont );
			alm.addCambioDeCapacidad( gen.nextInt(gapCapacidad) + capacidadMinimaAlmacen);
			cont.addAlmacen( alm );
			almacenes.add( alm );
		}
		// generar Conexiones
		Vector< Integer> pi = new Vector<>( nroAlmacenes );
		Vector< Integer > piHora = new Vector<> ( 24 );
		for( int i = 0 ; i < 24 ; i++ ) piHora.add( i );
		for( int i = 0 ; i < nroAlmacenes ; i++ ) pi.add( new Integer( i ) );
		for( int i = 0 ; i < nroAlmacenes ; i++ ){
			Collections.shuffle( pi );
			Collections.shuffle( piHora );
			int len = Math.max(nroAlmacenes / 5 * 4, gen.nextInt( nroAlmacenes ) + 1);
			// TODO: grafo completo
			len = nroAlmacenes;
			Almacen u = almacenes.get( i );
			for( int j = 0 ; j < len ; j++ ){
				Almacen v = almacenes.get( j );
				if( v.getNombre().compareTo( u.getNombre() ) == 0 ) continue;
				int conec_len = gen.nextInt( nroConexionesDiaPorAlmacen );
				Vector< Integer > horas = new Vector<>();
				//TODO: recontra completo
				conec_len = 24;
				while( conec_len-- > 0  )  horas.add( piHora.get( conec_len ) );
				Collections.sort( horas );
				for( Integer h : horas ){
					int capacidadConexion = gen.nextInt( gapConex ) + capacidadMinimaConexion;
					boolean [] freq = new boolean[ 7 ];
					//TODO: hyper completo
					for( int k = 0 ; k < 7 ; k ++ ) freq[ k ] = true;
					Conexion conex = new Conexion( u ,  v , h , freq, capacidadConexion);
					conexiones.add( conex );
				}
			}
		}
		for(int i = 0; i < nroAlmacenes; i++)
			for(int j = 0; j < nroAlmacenes; j++) if(i != j){
				medias.put(new AbstractMap.SimpleEntry<Almacen, Almacen>(almacenes.get(i), almacenes.get(j)), gen.nextInt(mediaMaxima));
				varianzas.put(new AbstractMap.SimpleEntry<Almacen, Almacen>(almacenes.get(i), almacenes.get(j)), gen.nextDouble() * varianzaMaxima);
			}
		
		// generar paquetes
		// tener en cuenta de que estos paquetes generados seran ingresados como semilla
		// para las inferencias que se haran con la DistribucionDiscretaConexion 
		// y por NOMBRE_ALGORITMO_XURRETA 
		for( Fecha it = new Fecha( 0 , 0 ) ; it.compareTo( fechaInicio ) < 0  ; it = Fecha.nextHora( it ) ){
			for( Almacen u : almacenes ){
				for( Almacen v : almacenes ) if( u.getNombre().compareTo( v.getNombre() ) != 0 ){
					int nro_paquetes = gen.nextInt( gapCapacidad ) + capacidadMinimaAlmacen;
					nro_paquetes = 3;
					while( nro_paquetes-- > 0 ){
						Paquete p = new Paquete( u , v, it , true );
						u.addPaqueteOriginadoEnAlmacen(p);
					}
				}
			}
		}
	}
	
	public void print(){
		File output = new File("input.txt");
		PrintStream outStream = null;
		try {
		  outStream = new PrintStream( output );
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		outStream.println("0. Fecha Inicio / Fin ");
		outStream.println( fechaInicio.toString());
		outStream.println( fechaFin.toString());
		
		outStream.println( "1. Continentes.");
		outStream.println(continentes.size());
		for( Continente c : continentes )
			outStream.println( c.getNombre() );
		
		outStream.println( "2. Almacenes.");
		outStream.println(almacenes.size());
		for( Almacen a : almacenes ) 
			outStream.println( a.getNombre() + "\n" + a.getContinente().getNombre() + "\n" + a.getCapacidadEnInstanteDeTiempo(fechaInicio));
		
		outStream.println("3. Conexiones.");
		outStream.println(conexiones.size());
		for( Conexion c : conexiones ){
			Almacen orig = c.getOrigen() , dest = c.getDestino();
			outStream.println( orig.getNombre() + "\n" + dest.getNombre() + "\n" + c.getHoraDeVuelo() +
					"\n" + c.getCapacidadEnInstanteDeTiempo( fechaInicio ) +  "\n" + c.getFrecuenciaComoString());
		}
		
		outStream.println("4. Distribuciones");
		for(Almacen a : almacenes)
			for(Almacen b: almacenes) if(a != b){
				outStream.println(a.getNombre());
				outStream.println(b.getNombre());
				outStream.println(medias.get(new AbstractMap.SimpleEntry<>(a, b)));
				outStream.println(varianzas.get(new AbstractMap.SimpleEntry<>(a, b)));
			}
	}
	
	public static void main( String [] args ){
		DataFactory x = new DataFactory();
		x.processRandom(2 , 20 ,3, 5, 2 , 100 , 200 , 500 ,600 , 3 , 4, 5, 2);
		x.print();
		/*try { x.processFromFiles("Continentes.txt", "Almacenes.txt", "Conexiones.txt", "Parametros.txt"); }
		catch ( Exception e ) { e.printStackTrace(); }
		x.print();*/
	}
	public int getMedia(Almacen u, Almacen v) {
		// TODO Auto-generated method stub
		return medias.get( new AbstractMap.SimpleEntry<>( u , v ) );
	}
	public double getVarianza(Almacen u, Almacen v) {
		// TODO Auto-generated method stub
		return varianzas.get( new AbstractMap.SimpleEntry<>( u , v ) );
	}
	
}
